#include "JsonRpcResponse.h"

JsonRpcResponse::JsonRpcResponse()
    : JsonRpcResponse(QVariant())
{
}

JsonRpcResponse::JsonRpcResponse(const QVariant &id)
    : JsonRpcMessage()
    , mResult(QJsonObject())
    , mError(QJsonObject())
{
    mId = id;
}

JsonRpcResponse::~JsonRpcResponse()
{
}

QJsonObject JsonRpcResponse::result() const
{
    return mResult;
}

void JsonRpcResponse::setResult(const QJsonObject &result)
{
    if (mResult != result) {
        mResult = result;
    }
    mError = QJsonObject();
}

QJsonObject JsonRpcResponse::error() const
{
    return mError;
}

void JsonRpcResponse::setError(const QJsonObject &error)
{
    if (mError != error) {
        mError = error;
    }
    mResult = QJsonObject();
}

QJsonObject JsonRpcResponse::toJson() const
{
    QJsonObject json;
    json["jsonrpc"] = jsonrpc();
    
    if (!mId.isNull()) {
        json["id"] = QJsonValue::fromVariant(mId);
    }
    
    if (!mError.isEmpty()) {
        json["error"] = mError;
    } else if (!mResult.isEmpty()) {
        json["result"] = mResult;
    }
    
    return json;
}

JsonRpcResponse JsonRpcResponse::fromJson(const QJsonObject &json, bool* ok)
{
    JsonRpcResponse response;
    
    if (!json.contains("jsonrpc") || json["jsonrpc"].toString() != response.jsonrpc()) {
        if (ok) *ok = false;
        return response;
    }
    
    // Parse ID
    if (json.contains("id")) {
        response.mId = json["id"].toVariant();
    } else {
        if (ok) *ok = false;
        return response;
    }
    
    // Parse result or error
    if (json.contains("result")) {
        response.mResult = json["result"].toObject();
        response.mError = QJsonObject();
    } else if (json.contains("error")) {
        response.mError = json["error"].toObject();
        response.mResult = QJsonObject();
    } else {
        // Neither result nor error present
        if (ok) *ok = false;
        return response;
    }
    
    if (ok) *ok = true;
    return response;
}

bool JsonRpcResponse::isValid() const
{
    // Must have either result or error
    if (mError.isEmpty() && mResult.isEmpty()) {
        return false;
    }
    
    // If error is present, it must be a valid error object
    if (!mError.isEmpty()) {
        if (!mError.contains("code") || !mError.contains("message")) {
            return false;
        }
        
        // Code must be integer
        if (!mError["code"].isDouble()) {
            return false;
        }
        
        // Message must be string
        if (!mError["message"].isString()) {
            return false;
        }
    }
    
    return true;
}
